/*
 * Copyright (C) 2012 Matt Broadstone
 * Contact: http://code.google.com/p/qconnman/
 *
 * This file is part of the QConnman Library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */
#ifndef MANAGER_H
#define MANAGER_H

#include <QAbstractItemModel>
#include <QStringList>
#include <QList>
#include <QPointer>
#include <QDBusVariant>
#include <QDBusArgument>

#include "agent.h"
#include "technology.h"
#include "service.h"

class ManagerNode
{
public:
    ManagerNode();
    ManagerNode(Technology *technology, ManagerNode *parent = 0);
    ManagerNode(Service *service, ManagerNode *parent = 0);
    ~ManagerNode();

    bool isTechnology() const;
    bool isService() const;
    bool isRoot() const;
    QDBusObjectPath path() const;

    template<typename T>
    inline T object() const { return qobject_cast<T>(m_object); }

    ManagerNode *parent();
    ManagerNode *child(int index);
    void appendChild(ManagerNode *node);
    int childCount() const;
    int childNumber() const;
    bool removeChildren(int position, int count);

private:
    ManagerNode *m_parent;
    QList<ManagerNode*> m_children;
    QPointer<QObject> m_object;
    bool m_technology;

};
Q_DECLARE_METATYPE(ManagerNode*)

class ManagerInterface;
class QDBusServiceWatcher;
class QDBusPendingCallWatcher;
class Manager : public QAbstractItemModel
{
    Q_OBJECT

    Q_ENUMS(Manager::State)
    Q_PROPERTY(QString State READ state WRITE setStateInternal NOTIFY stateChanged)
    Q_PROPERTY(bool OfflineMode READ offlineMode WRITE setOfflineModeInternal NOTIFY offlineModeChanged)
    Q_PROPERTY(bool SessionMode READ sessionMode WRITE setSessionModeInternal NOTIFY sessionModeChanged)

public:
    enum ManagerDataRoles { ManagerNodeRole = Qt::UserRole + 1 };
    
    Manager(QObject *parent = 0);
    ~Manager();

    enum State {
      Offline,
      Idle,
      Ready,
      Online
    };
    State state() const;

    bool offlineMode() const;
    void setOfflineMode(bool offlineMode);

    bool sessionMode() const;
    void setSessionMode(bool sessionMode);

    QList<Agent*> agents() const;
    QList<Technology*> technologies() const;
    QList<Service*> services() const;

    bool hasService(const QString &name) const;
    bool hasService(const QDBusObjectPath &path) const;
    Service *service(const QString &name);
    Service *service(const QDBusObjectPath &path);

    Service *connectedService();
    void disconnectServices();
    // QAbstractItemModel specific
    QVariant data(const QModelIndex &index, int role) const;
    QModelIndex index(int row, int column,
                      const QModelIndex &parent = QModelIndex()) const;
    QModelIndex parent(const QModelIndex &index) const;
    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    int columnCount(const QModelIndex &parent = QModelIndex()) const;

Q_SIGNALS:
    void stateChanged();
    void offlineModeChanged();
    void sessionModeChanged();

public Q_SLOTS:
    void registerAgent(Agent *agent);
    void unregisterAgent(Agent *agent);
    void unregisterAgent(const QDBusObjectPath &path);

private Q_SLOTS:
    void connmanRegistered();
    void connmanUnregistered();

    void propertyChanged(const QString &name, const QDBusVariant &value);
    void servicesChanged(const QList<ObjectPropertyData> &changedServices,
                         const QList<QDBusObjectPath> &removedServices);

    void technologyAdded(const QDBusObjectPath &path, const QVariantMap &properties);
    void technologyRemoved(const QDBusObjectPath &path);

    void getTechnologiesResponse(QDBusPendingCallWatcher *call);
    void getServicesResponse(QDBusPendingCallWatcher *call);
    void getPropertiesResponse(QDBusPendingCallWatcher *call);

    void setObjectProperty(QObject *object, const QString &property, const QVariant &value);

private:
    void initialize();
    Technology *technologyForType(const QString &type);
    ManagerNode *nodeForIndex(const QModelIndex &index) const;
    ManagerNode *nodeForPath(const QDBusObjectPath &path, ManagerNode *parent) const;
    ManagerNode *nodeForTechnologyType(const QString &type);
    void setStateInternal(const QString &state);
    void setOfflineModeInternal(bool offlineMode);
    void setSessionModeInternal(bool sessionMode);

    static QHash<QString, Manager::State> s_stateLookup;

    ManagerInterface *m_managerInterface;
    QDBusServiceWatcher *m_connmanWatcher;

    State m_state;
    bool m_offlineMode;
    bool m_sessionMode;

    QHash<QDBusObjectPath, Agent*> m_agents;
    QList<Technology*> m_technologies;
    QHash<QDBusObjectPath, Service*> m_services;
    ManagerNode *m_root;
};


#endif

